
//1.currying
//假设我们要编写一个计算每月开销的函数。在每天结束之前，我们都要记录今天花掉了多少钱”
    var monthlyCost = 0;
	var cost = function( money ){
		monthlyCost += money;
	};

	cost( 100 ); // 第1 天开销
	cost( 200 ); // 第2 天开销
	cost( 300 ); // 第3 天开销
	//cost( 700 ); // 第30 天开销
	alert ( monthlyCost ); // 输出：600



	var cost = (function(){
		var args = [];
		return function(){
			if ( arguments.length === 0 ){
				var money = 0;
				for ( var i = 0, l = args.length; i < l; i++ ){
					money += args[ i ];
					50 第3 章 闭包和高阶函数
				}
				return money;
			}else{
				[].push.apply( args, arguments );
			}
		}
	})();

	cost( 100 ); // 未真正求值
	cost( 200 ); // 未真正求值
	cost( 300 ); // 未真正求值
	console.log( cost() ); // 求值并输出：600


    //我们编写一个通用的function currying(){}，function currying(){}接受一个参数，即将要被currying的函数。”
	var currying = function( fn ){
		var args = [];
		return function(){
			if ( arguments.length === 0 ){
				return fn.apply( this, args );
			}else{
				[].push.apply( args, arguments );
				return arguments.callee;
			}
		}
	};
	var cost = (function(){
		var money = 0;
		return function(){
			for ( var i = 0, l = arguments.length; i < l; i++ ){
				money += arguments[ i ];
			}
			return money;
		}
	})();

	var cost = currying( cost ); // 转化成currying 函数
	cost( 100 ); // 未真正求值
	cost( 200 ); // 未真正求值
	cost( 300 ); // 未真正求值
	alert ( cost() ); // 求值并输出：600

    //2. uncurrying
    //一个对象也未必只能使用它自身的方法，那么有什么办法可以让对象去借用一个原本不属于它的方法呢？”

    //我们常常让类数组对象去借用Array.prototype的方法，这是call和apply最常见的应用场景之一”

    (function(){
        Array.prototype.push.call( arguments, 4 );    // arguments借用Array.prototype.push方法
        console.log( arguments );      // 输出：[1, 2, 3, 4]
    })( 1, 2, 3 );
    
    //有没有办法把泛化this的过程提取出来呢？”
    Function.prototype.uncurrying = function () {
		var self = this;
		return function() {
			var obj = Array.prototype.shift.call( arguments );
			return self.apply( obj, arguments );
		};
	};

    //在类数组对象arguments借用Array.prototype的方法之前，先把Array.prototype.push.call这句代码转换为一个通用的push函数：”

    var push = Array.prototype.push.uncurrying();

	(function(){
		push( arguments, 4 );
		console.log( arguments ); // 输出：[1, 2, 3, 4]
	})( 1, 2, 3 );

    //我们还可以一次性地把Array.prototype上的方法“复制”到array对象上，同样这些方法可操作的对象也不仅仅只是array对象

    for ( var i = 0, fn, ary = [ 'push', 'shift', 'forEach' ]; fn = ary[ i++ ]; ){
		Array[ fn ] = Array.prototype[ fn ].uncurrying();
	};
	var obj = {
		"length": 3,
		"0": 1,
		"1": 2,
		"2": 3
	};

	Array.push( obj, 4 ); // 向对象中添加一个元素
	console.log( obj.length ); // 输出：4
	var first = Array.shift( obj ); // 截取第一个元素
	console.log( first ); // 输出：1
	console.log( obj ); // 输出：{0: 2, 1: 3, 2: 4, length: 3}

	Array.forEach( obj, function( i, n ){
		console.log( n ); // 分别输出：0, 1, 2
	});



    var call = Function.prototype.call.uncurrying();

	var fn = function( name ){
		console.log( name );
	}
    //fn.call( window,'sven' ); // 输出：sven
	call( fn, window, 'sven' ); // 输出：sven

	var apply = Function.prototype.apply.uncurrying();
	var fn = function( name ){
		console.log( this.name ); // 输出："sven"
		console.log( arguments ); // 输出: [1, 2, 3]
	};

    //fn.apply({ name: 'sven' }, [ 1, 2, 3 ])
	apply( fn, { name: 'sven' }, [ 1, 2, 3 ] );


    //除了刚刚提供的代码实现，下面的代码是uncurrying的另外一种实现方式”
	Function.prototype.uncurrying = function(){
		var self = this;
		return function(){
			return Function.prototype.call.apply( self, arguments );
		}
	};
	var push = Array.prototype.push.uncurrying();
	(function(){
		push( arguments, 4 );
		console.log( arguments ); // 输出：[1, 2, 3, 4]
	})( 1, 2, 3 );

	//等价于
	(function(){
		Function.prototype.call.apply(Array.prototype.push , [arguments,4]);
		console.log( arguments ); // 输出：[1, 2, 3, 4]
	})( 1, 2, 3 );


	// 3. 函数节流
	var throttle = function ( fn, interval ) {
		var __self = fn, // 保存需要被延迟执行的函数引用
		timer, // 定时器
		firstTime = true; // 是否是第一次调用
		return function () {
			var args = arguments,
			__me = this;
			if ( firstTime ) { // 如果是第一次调用，不需延迟执行
				__self.apply(__me, args);
				return firstTime = false;
			}
			if ( timer ) { // 如果定时器还在，说明前一次延迟执行还没有完成
				return false;

			timer = setTimeout(function () { // 延迟一段时间执行
				clearTimeout(timer);
				timer = null;
				__self.apply(__me, args);
			}, interval || 500 );
		};
	};

	window.onresize = throttle(function(){
		console.log( 1 );
	}, 500 ); // 输出：1 500ms后输出：1

	//4. 分时函数”
	//timeChunk函数，timeChunk 函数让创建节点的工作分批进行，比如把1秒钟创建1000个节点，改为每隔200毫秒创建8个节点。
	//timeChunk函数接受3个参数，第1个参数是创建节点时需要用到的数据，第2个参数是封装了创建节点逻辑的函数，第3个参数表示每一批创建的节点数量。

	var timeChunk = function( ary, fn, count ){
		var obj,
		t;
		var len = ary.length;
		var start = function(){
			for ( var i = 0; i < Math.min( count || 1, ary.length ); i++ ){
				var obj = ary.shift();
				fn( obj );
			}
		};
		return function(){
			t = setInterval(function(){
			if ( ary.length === 0 ){ // 如果全部节点都已经被创建好
				return clearInterval( t );
			}
			start();
			}, 200 ); // 分批执行的时间间隔，也可以用参数的形式传入
		};
	};

	var ary = [];
	for ( var i = 1; i <= 1000; i++ ){
		ary.push( i );
	};
	var renderFriendList = timeChunk( ary, function( n ){
		var div = document.createElement( 'div' );
		div.innerHTML = n;
		document.body.appendChild( div );
	}, 8 );
	renderFriendList(); // 每隔200毫秒创建8个节点

	//5. 惰性加载函数

	//我们把嗅探浏览器的操作提前到代码加载的时候，在代码加载的时候就立刻进行一次判断，以便让addEvent返回一个包裹了正确逻辑的函数。”

	var addEvent = function( elem, type, handler ){
		if ( window.addEventListener ){
			return elem.addEventListener( type, handler, false );

		}
		if ( window.attachEvent ){
			return elem.attachEvent( 'on' + type, handler );
		}
	};

	//第三种方案即是我们将要讨论的惰性载入函数方案。此时addEvent依然被声明为一个普通函数，在函数里依然有一些分支判断。但是在第一次进入条件分支之后，在函数内部会重写这个函数，重写之后的函数就是我们期望的addEvent函数，在下一次进入addEvent函数的时候，addEvent函数里不再存在条件分支语句：”

	var addEvent = function( elem, type, handler ){
		if ( window.addEventListener ){
			addEvent = function( elem, type, handler ){
				elem.addEventListener( type, handler, false );
			}
		}else if ( window.attachEvent ){
			addEvent = function( elem, type, handler ){
				elem.attachEvent( 'on' + type, handler );
			}
		}
		addEvent( elem, type, handler );
	};
	
